#include "FileTransfer.h"
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include "log.h"
#include <iostream>
#include "VideoFileStream.h"
#include "PictureFileStream.h"
#include <algorithm>

#include <dirent.h>
#include "mediaPriv.h"
#include "unzip.h"
#include "StreamerCurtainPlay.h"
#include <iostream>
#include <iomanip>
#include "WlanService.h"
#include <thread>
#include "ScreenMirrorStream.h"
#include "ZipUtil.h"

extern "C"
{
	#include "md5.h"
}

using namespace std;
using namespace Media;
using namespace Fdt;
FileTransfer 	*FileTransfer::sm_file_transfer = nullptr;
mutex			FileTransfer::sm_fileMutex;



typedef struct 
{
    unzFile unzfile;                    /* 压缩包句柄 */
    unz_global_info glbInfo;            /* 压缩包信息 */
}Image_Ctl_t;


FileTransfer::FileTransfer(){
	m_unzip = false;
}

//判断一下文件是否已经传输过了,并且确定文件的保存路径
bool FileTransfer::fileStreamReset(string filename, string Type){
	//printf("entry access\n");
	m_filename = filename;
	
	string saveDir;
	m_type = Type;
	if(!DiskController::instance()->setRealTimeRootPath()){
		LOG_PrintError("faild get root path\n");
		return false;
	}
	if(!getFileDir(filename, Type, saveDir))
	{
	    LOG_PrintError("getFileDir error\n");
        return false;
	}

	
	std::string path;
	DiskController::instance()->getRealTimeBusinessPath(STREAM_ROOT_DIR, path);
	if(saveDir == path){
		//清理旧的横幅文件
		DiskController::instance()->clearStreamerFiles(STREAM_ROOT_DIR);
	}
	
	string file_path = saveDir + filename;
	if(access(file_path.c_str(), F_OK) == 0){
		//printf("remove access\n");
		m_flag_retransfer = true;
	}else{
		m_flag_retransfer = false;
	}
	return true;
}

bool FileTransfer::streamWrite(string filename, const void *buffer, size_t size){
	
	string saveDir;
	if(!getFileDir(filename, m_type, saveDir))
	{
	    LOG_PrintError("getFileDir error\n");
        return false;
	}
	
	string file_path = saveDir + "/" +filename;
	//LOG_PrintInfo("filename:%s\n", file_path.c_str());

	//如果之前有传输过相同的文件就需要删除
	if(m_flag_retransfer && !filename.compare(m_filename)){
		//printf("remove access\n");
		string new_name = saveDir + "_copy" + filename;
		int ret =  rename(file_path.c_str(), new_name.c_str());
		if(ret != 0){
			LOG_PrintError("remove file faild error code:%d\n", ret);
		}
		m_flag_retransfer = false;
	}
	if(m_fd < 0){
		m_fd = open((const char*)file_path.c_str(),O_RDWR|O_CREAT|O_APPEND , 0777);
		if (m_fd < 0)
		{
			LOG_PrintError("open %d failed!\n", m_fd);
			return false;
		}
	}

	int len = write(m_fd, buffer, size);
	if(len < 0){
		if(m_fd >= 0){
			close(m_fd);
			m_fd = -1;
		}
		LOG_PrintError("file write failed!\n");
	}

	return len >= 0;
}

//open close
bool FileTransfer::fileVerify(string filename, const char *md5){
	bool ret = true;
	unsigned int size;
	unsigned char *pData;
	unsigned char outputTemp[16] = {0};
	const char *md = md5;
	ZipUtil zip;
	
	if(m_fd >= 0){
		close(m_fd);
		m_fd = -1;
	}
	
	string saveDir;
	if(!getFileDir(filename, m_type, saveDir))
	{
	    LOG_PrintError("getFileDir error\n");
        return false;
	}
	
	string file_name = saveDir + "/" +filename;
	LOG_PrintInfo("filename:%s\n", file_name.c_str());
	//新的方式打开，否则会出现段错误
	int fd = open(file_name.c_str(),O_RDWR);
	if (fd < 0)
	{
		printf("open %d failed!\n", fd);
		return false;
	}
	size = lseek(fd, 0, SEEK_END);

	pData = (unsigned char *)mmap(NULL,
								  size,
								  PROT_READ,
								  MAP_PRIVATE,
								  fd,
								  0);
	md5_calc(outputTemp, pData, size);

	uint8_t *mddd = (uint8_t *)md;
	printf("local md--->%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X \n", mddd[0],mddd[1], mddd[2], mddd[3], mddd[4],
				mddd[5], mddd[6], mddd[7], mddd[8], mddd[9], mddd[10], mddd[11], mddd[12], mddd[13], mddd[14], mddd[15]);
	printf("local md--->%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X \n", outputTemp[0],outputTemp[1], outputTemp[2], outputTemp[3], outputTemp[4],
				outputTemp[5], outputTemp[6], outputTemp[7], outputTemp[8], outputTemp[9], outputTemp[10], outputTemp[11], outputTemp[12], outputTemp[13], outputTemp[14], outputTemp[15]);

	if (memcmp(outputTemp, md5, 16) == 0)
	{
		LOG_PrintInfo("Download succeed.\n");
		remove((saveDir + "_copy" + filename).c_str());
		//解压
		if(m_unzip){
			if(zip.unZipFile(file_name, saveDir)){
			    munmap((void *)pData, size);
			    close(fd);
				return false;
			}
			int ret = remove(file_name.c_str());
			if(ret != 0){
				LOG_PrintError("remove file faild error code:%d\n", ret);
			}
			m_unzip = false;
			//开启横幅场景
			if(saveDir.find(AIR_FLASH_PATH) != std::string::npos){
				Media::StreamerCurtainPlay::instance()->playStreamerCurtain(CLIENT_STREAMER, AIR_FLASH_PATH);
			}else{
				Media::StreamerCurtainPlay::instance()->playStreamerCurtain(CLIENT_STREAMER, AIR_UDISK_PATH);
			}
		}
	}
	else
	{
		int ret = remove(filename.c_str());
		if(ret != 0){
			LOG_PrintError("remove file faild error code:%d\n", ret);
		}
		ret = rename((saveDir + "_copy" + filename).c_str(), (file_name + "_copy").c_str());
		if(ret != 0){
			LOG_PrintError("rename file faild error code:%d\n", ret);
		}
		LOG_PrintInfo("Download failed.\n");
		ret = false;
	}

	munmap((void *)pData, size);
    close(fd);
	return ret;
}

bool FileTransfer::getFileDir(string filename, string Type, string &fileDir)
{
	int start = filename.find(".");
	if(start == filename.npos){
		LOG_PrintError("filename Error %s\n", filename.c_str());
		return false;
	}
	string postfix = filename.substr(start + 1, filename.length() - start - 1);
	transform(postfix.begin(), postfix.end(), postfix.begin(), ::tolower);
	if(Type == m_curtain){		
		//提出来放后面
		if(postfix == "jpg"){
			fileDir = picturePath;
			
		}else if(postfix == "mp4"){
			fileDir = videoPath;
		}else{
			LOG_PrintError("filename Error %s cannot support format\n", filename.c_str());
			return false;
		}
	}else if(Type == m_streamer){

        ///判定设备当前是否在投屏
        if(Media::ScreenMirrorStream::instance()->getShareScreenStatus())
        {
            std::string errinfo = "The device is displaying screen mirroring information, please try again later.";
            LOG_PrintError("DigitalSignage faild, errinfo=%s\n", errinfo.c_str());
    		return false;        
        }

        Media::StreamerCurtainPlay::instance()->stopStreamerCurtain();//先停止播放
        
		//zip文件判定
		if(postfix != "zip"){
			LOG_PrintError("file format error: %s\n", postfix.c_str());
			return false;
		}
		//设置压缩文件的存储位置
		DiskController::instance()->getRealTimeBusinessPath(STREAM_ROOT_DIR, fileDir);
		//设置解压标记
		m_unzip = true;
	}

	DIR* fpDir = opendir(fileDir.c_str());
	if(!fpDir)
	{
		mkdir(fileDir.c_str(), 0775);
		if(!(fpDir = opendir(fileDir.c_str())))
        {
            errorf("Folder opendir failed!");
            return false;
        }
		closedir(fpDir);
	}
	else
	{
		closedir(fpDir);		
	}
	return true;
}



bool FileTransfer::GetFileInfo(Json::Value table ,string &filename, unsigned int &size, string &md5){
	//获取传下来的type
	if(!table.isMember("Type") || !table["Type"].isString()){
		LOG_PrintError("parameter error\n");
		return false;
	}
	//通过传下来的type找到对应的文件
	string type = table["Type"].asString();
	if(type == m_streamer){
		std::string streamerPath;
		DiskController::instance()->getRealTimeBusinessPath(STREAM_ROOT_DIR, streamerPath);
		//获取文件路径
		if(!getFileName(streamerPath, ".zip", filename)){
			LOG_PrintError("get filename faild\n");
			return false;
		}
		string filePath = streamerPath + "/" + filename;
		ZipUtil zip;
		if(!zip.getMd5(filePath, size, md5)){
			LOG_PrintError("get filename faild\n");
			return false;
		}
		return true;
	}
	return true;
}

bool FileTransfer::getFileName(string path, string postfix, string &filename){
	
	DIR* dp;
	struct dirent* ep;
	dp = opendir(path.c_str());
	if (dp == NULL)
	{	
		LOG_PrintError("open directory /dev faild!!\n");
		return false;		
	}
	while (ep = readdir(dp)){
		string fileName = ep->d_name;
		// 判断文件名中是否包含后缀
		if(fileName.find(postfix) != std::string::npos){
			filename = fileName;
			closedir(dp);
			return true;
		}
	}
	closedir(dp);
	return false;
}




bool FileTransfer::CloudUploadStream(Json::Value table, function<bool(string file, string url, function<void(const SockException &ex)> cb)> callback){
	//判断参数
	if(!table.isMember("Filename") || !table["Filename"].isString()){
		return false;
	}
	string filename = table["Filename"].asString();
	if(!table.isMember("Md5") || !table["Md5"].isString()){
		return false;
	}
	string resmd5 = table["Md5"].asString();
	std::string md5 = strHex2Val(resmd5.c_str(), resmd5.size());
	if(!table.isMember("Type") || !table["Type"].isString()){
		return false;
	}

	string type = table["Type"].asString();
	m_type = type;
	if(!table.isMember("Url") || !table["Url"].isString()){
		return false;
	}
	string url = table["Url"].asString();	
	LOG_PrintInfo("filename: %s    type: %s    url: %s   md5: %s\n", filename.c_str(), type.c_str(), url.c_str(), md5.c_str());
#if 0
	//接口不准确
	//判断外网是否通
	if(!WlanService::instance()->checkInternetPing()){
		LOG_PrintError("Cannot connect Internet\n");
		return false;
	}
#endif
	//获取文件的存储路径
	string fileDir;
	if(!getFileDir(filename, type, fileDir)){
		LOG_PrintError("UnSupport FileType or Direction not exist\n");
		return false;
	}
	string filePath = fileDir + "/" + filename;
	//清理旧的横幅文件
	if(!true){
		LOG_PrintError("cannot remove old file\n");
		return false;
	}
	//根据URL下载文件，注册成功回调和失败的回调
	m_md5_val = md5;
	static auto resultCb = [filename, md5, this](const SockException &ex){
		  LOG_PrintInfo("----------static auto resultCb\n");
		  if (ex) {
            //网络相关的错误
          	WarnL << "network err:" << ex.getErrCode() << " " << ex.what();
			setCloudUpMark(-1);
			WarnL << "network err:" << "      " << getCloudUpMark();
		  } else {
			WarnL << "succeed"  << ex.what();
			if(!fileVerify(filename, m_md5_val.c_str())){
				LOG_PrintWarn("fileVerify faild!\n");
			}
			setCloudUpMark(1);
			WarnL << "succeed" << "      " << getCloudUpMark();
		  }
	};
	callback(filePath, url, resultCb);
	//解压
	//播放场景
	
	//发送请求到web端，请求最新的文件上传地址
	return true;
}

bool FileTransfer::exeCmd(string cmd){
	FILE *fp;
	//配置文件删除并且重启设备
	fp = popen(cmd.c_str(), "r");
	if(!fp){
		errorf("remove old Config Error popen faild\n");
		return false;
	}else{
		pclose(fp);
	}
	return true;
}

int FileTransfer::getCloudUpMark(){
	return m_cloudUpMark;
}
	
bool FileTransfer::setCloudUpMark(int mark){
	m_cloudUpMark = mark;
	return true;
}
	

